package com.wzw.expression.expression.evaluator;

import com.wzw.expression.expression.object.GeneralEvaluationContext;
import com.wzw.expression.expression.object.GeneralExpressionRootObject;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.lang.NonNull;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

/**
 * TypeLocator
 * T(com.wzw.record.CrmRecord)
 * 通常需要写全路径名称，这个可以添加包名。
 * ExpressionContext setTypeLocator
 *
 * @author Wangzhiwen
 */
public abstract class GeneralExpressionEvaluator extends CachedExpressionEvaluator {

    /**
     * 解析模板对象
     */
    private ParserContext parserContext;

    /**
     * spring bean
     */
    private BeanFactoryResolver beanFactoryResolver;

    public void setBeanFactoryResolver(BeanFactoryResolver beanFactoryResolver) {
        this.beanFactoryResolver = beanFactoryResolver;
    }

    public void setParserContext(ParserContext parserContext) {
        this.parserContext = parserContext;
    }

    /**
     * 创建SPEL上下文对象
     *
     * @param method       代理方法
     * @param args         参数
     * @param target       目标对象
     * @param targetClass  目标对象类型
     * @param targetMethod 目标方法
     * @return context
     */
    @SuppressWarnings("unchecked")
    public <T extends GeneralEvaluationContext> T createEvaluationContext
    (
            Method method,
            Object[] args,
            Object target,
            Class<?> targetClass,
            Method targetMethod,
            Class<T> clazz
    ) {

        GeneralExpressionRootObject rootObject = getExpressionRootObject(method, args, target, targetClass);
        GeneralEvaluationContext evaluationContext = getEvaluationContext(rootObject, targetMethod, args, getParameterNameDiscoverer());
        if (Objects.nonNull(beanFactoryResolver)) {
            evaluationContext.setBeanResolver(beanFactoryResolver);
        }
        return (T) evaluationContext;
    }

    @NonNull
    @Override
    protected Expression parseExpression(@NonNull String expression) {
        return getParser().parseExpression(expression, parserContext);
    }

    /**
     * 解析转化表达式
     *
     * @param cache      表达式缓存
     * @param expression 表达式
     * @param elementKey 方法key
     * @param context    上下文对象
     * @param clazz      值类型
     * @param <T>        类型
     * @return T
     */
    protected <T> T resolve(Map<ExpressionKey, Expression> cache, String expression, AnnotatedElementKey elementKey, GeneralEvaluationContext context, Class<T> clazz) {
        return getExpression
                (
                        cache,
                        elementKey,
                        expression
                ).getValue(context, clazz);
    }

    /**
     * 创建表达式根对象
     *
     * @param method      代理方法
     * @param args        入参
     * @param target      目标对象
     * @param targetClass 目标对象类型
     * @return GeneralExpressionRootObject
     */
    protected abstract GeneralExpressionRootObject getExpressionRootObject(Method method, Object[] args, Object target, Class<?> targetClass);

    /**
     * 创建执行上下文对象
     *
     * @param rootObject   表达式根对象
     * @param targetMethod 目标方法
     * @param args         入参
     * @param discoverer   参数名称发现者
     * @return GeneralEvaluationContext
     */
    protected abstract GeneralEvaluationContext getEvaluationContext(GeneralExpressionRootObject rootObject, Method targetMethod, Object[] args, ParameterNameDiscoverer discoverer);

    /**
     * 清除缓存
     */
    public abstract void clear();

}
